package com.zys.zys_mybatis;

import com.zys.zys_mybatis.utils.LocalDruidUtil;
import com.zys.zys_mybatis.utils.StringUtil;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

/**
 * @BelongsProject: ZYSMybatis
 * @BelongsPackage: com.zys.mappers
 * @Author: ZYS
 * @CreateTime: 2024-05-24  21:20
 * @Description: TODO
 */
public class MapperProxyFactory {
    public MapperProxyFactory() {
        //连接初始化，让第一次查询更快
        new LocalDruidUtil();
    }

    public <T> T getMapper(Class<T> mapperInterface) {
        Object o = null;
        try {
            o = Proxy.newProxyInstance(
                    MapperProxyFactory.class.getClassLoader(),
                    new Class[]{mapperInterface},
                    (proxy, method, args) -> {
                        //获取数据库连接
                        Connection connection = LocalDruidUtil.getConnection();

                        //从注解中获得sql语句
                        Select annotation = method.getAnnotation(Select.class);
                        assert annotation != null;
                        String sql0 = annotation.value();
                        System.out.println(sql0);

                        PreparedStatement preparedStatement = null;
                        //判断是否有参数
                        if (sql0.contains("#")) {
                            //解析sql语句
                            //若有参数未添加@Param注解，则报错
                            Parameter[] parameters = method.getParameters();
                            for (Parameter parameter : parameters) {
                                if (parameter.getAnnotation(Param.class) == null) {
                                    throw new RuntimeException("参数个数与sql占位符不匹配");
                                }
                            }

                            //将参数名和参数值放入map中,key为参数名，value为参数值
                            Map<String, Object> map = new HashMap<>();
                            for (int i = 0; i < parameters.length; i++) {
                                map.put(parameters[i].getAnnotation(Param.class).value(), args[i]);
                                map.put(parameters[i].getName(), args[i]);
                            }

                            //将参数名替换为?占位符，这里仅针对#{}
                            for (Parameter parameter : parameters) {
                                //获取参数名
                                String paramName = parameter.getAnnotation(Param.class).value();
                                //替换参数名
                                sql0 = sql0.replace("#{" + paramName + "}", "?");
                            }
                            //创建PreparedStatement对象
                            preparedStatement = connection.prepareStatement(sql0);
                            for (int i = 0; i < args.length; i++) {
                                preparedStatement.setObject(i + 1, args[i]);
                            }
                        } else {
                            preparedStatement = connection.prepareStatement(sql0);
                        }
                        //执行查询
                        ResultSet resultSet = preparedStatement.executeQuery();
                        //利用反射，将结果集中的数据封装到对象中
                        //获取返回对象类型
                        Class<?> returnType = method.getReturnType();
                        //判断返回类型是否为List,若是，则创建List对象,并封装List数据
                        if (returnType.equals(List.class)) {
                            //获取泛型类型
                            Class<?> listReturnType = (Class<?>) ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments()[0];
                            List<Object> returnList = new ArrayList<>();
                            //获取获取BeanInfo对象,排除Object父类属性
                            BeanInfo beanInfo = Introspector.getBeanInfo(listReturnType, Object.class);
                            //获取属性描述符
                            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
                            //遍历
                            while (resultSet.next()) {
                                Object o1 = listReturnType.newInstance();
                                //将resultSet中数据映射到list中一个对象里
                                resultSetToPojo(resultSet, o1, propertyDescriptors);
                                returnList.add(o1);
                            }
                            //关闭资源
                            LocalDruidUtil.closeResource(resultSet, preparedStatement, connection);
                            return returnList;
                        }
                        if (!resultSet.next()) {
                            //关闭资源
                            LocalDruidUtil.closeResource(resultSet, preparedStatement, connection);
                            return null;
                        }
                        //创建返回对象
                        Object returnInstance = returnType.newInstance();
                        //获取获取BeanInfo对象,排除Object父类属性
                        BeanInfo beanInfo = Introspector.getBeanInfo(returnType, Object.class);
                        //获取属性描述符
                        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
                        //遍历属性描述符
                        if (propertyDescriptors == null) {
                            return null;
                        }
                        resultSetToPojo(resultSet, returnInstance, propertyDescriptors);
                        //关闭资源
                        LocalDruidUtil.closeResource(resultSet, preparedStatement, connection);
                        return returnInstance;
                    });
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return (T) o;
    }

    private void resultSetToPojo(ResultSet resultSet, Object o1,
                                 PropertyDescriptor[] propertyDescriptors)
            throws SQLException, InvocationTargetException, IllegalAccessException {
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            //获取属性名
            String propertyName = StringUtil.camelToUnderline(propertyDescriptor.getName());
            //获取属性值
            Object propertyValue = resultSet.getObject(propertyName);
            if (propertyValue != null) {
                //将属性值设置到对象中
                propertyDescriptor.getWriteMethod().invoke(o1, propertyValue);
            }
        }
    }
}
